﻿//===============================================================================
// Magic Framework
//===============================================================================
// Copyright ?Magic Thought Corporation. All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// Richie (http://www.cnblogs.com/RicCC)
// 3/10/2008 5:46:04 AM
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace SQLAnalyticLibrary.Expressions
{
    public enum NameResolverResult
    {
        /// <summary>
        /// I'm sure it's not mine.
        /// </summary>
        No = 0,
        /// <summary>
        /// I'm sure it's mine, and I've found the exact table name for you.
        /// </summary>
        Yes = 1,
        /// <summary>
        /// Maybe it's mine, and maybe it's others's
        /// </summary>
        Maybe = 2,
        /// <summary>
        /// I'm sure it's mine, but I cannot find the exact table name, it may be one of those in the maybeHere.
        /// </summary>
        MustBe = 3
    }

    public class QueryStatement : CombinedKeywords, ICompileVisitor
    {
        private bool _isTopStatement = false;
        private bool _needParen = true;
        protected bool _isCompiled = false;
        internal IList<QueryStatement> _subQueries;
        protected IList<UserVariable> _positionedVariables; //asign index number to each positioned variable.
        internal IList<Table> _tables; //gathered in compilation time, for quick seaching
        internal IList<Column> _columns; //gathered in compilation time, for quick seaching

        #region constructors
        /// <summary>
        /// SelectStmt is a imaginary token. Don't need combine subsequent tokens.
        /// </summary>
        /// <param name="node"></param>
        public QueryStatement(IToken t)
            : base(t, string.Empty, false, null, false, null)
        {
        }
        /// <summary>
        /// SelectStmt is a imaginary token. Don't need combine subsequent tokens.
        /// </summary>
        /// <param name="t"></param>
        public QueryStatement(IToken t, string text)
            : base(t, text, false, null, false, null)
        {
        }
        #endregion

        #region compile handlers
        private void CollectTables(FromClause clause)
        {
            if (clause == null) return;
            IList<Table> tables = clause.Tables;
            foreach (Table t in tables)
                this._tables.Add(t);
        }
        private void CollectColumns(SelectClause clause)
        {
            if (clause == null) return;
            IList<Column> columns = clause.Columns;
            foreach (Column c in columns)
                this._columns.Add(c);
        }
        public override void VisitCompile(ICompileVisitor visitor)
        {
            if (this._isCompiled) return;

            //initialization
            if (this._subQueries != null) this._subQueries.Clear();
            else this._subQueries = new List<QueryStatement>();
            if (this._positionedVariables != null) this._positionedVariables.Clear();
            else this._positionedVariables = new List<UserVariable>();
            if (this._tables != null) this._tables.Clear();
            else this._tables = new List<Table>();
            if (this._columns != null) this._columns.Clear();
            else this._columns = new List<Column>();

            //compilation visit
            foreach (Expression exp in this._children)
            {
                exp.VisitCompile(this);
                if (FromClause.IsFromClause(exp)) this.CollectTables(exp as FromClause);
                if (SelectClause.IsSelectClause(exp)) this.CollectColumns(exp as SelectClause);
            }

            //post compilation process
            if (!object.ReferenceEquals(this, visitor))
            {
                //sub queries don't need reference positioned variables
                foreach (UserVariable variable in this._positionedVariables)
                    visitor.VisitPositionedVariable(variable);
                this._positionedVariables.Clear();
                this._positionedVariables = null;
                visitor.VisitQuery(this);
            }
            else
            {
                this._isTopStatement = true; //This is not a good method to determine the top statement.
                for (int i = 0; i < this._positionedVariables.Count; i++)
                    this._positionedVariables[i].Position = i;
            }

            this._isCompiled = true;
            this.PostVisitCompile();
        }
        protected virtual void PostVisitCompile()
        {
        }
        #endregion

        #region members of ICompileVisitor
        /// <summary>
        /// Compile a query.
        /// </summary>
        /// <param name="stmt"></param>
        public void VisitQuery(QueryStatement statement)
        {
            if (this._subQueries == null)
                this._subQueries = new List<QueryStatement>();
            this._subQueries.Add(statement);
        }
        /// <summary>
        /// Compile a positioned variable.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="var"></param>
        public void VisitPositionedVariable(UserVariable var)
        {
            this._positionedVariables.Add(var);
        }
        #endregion

        public int SubQueryCount
        {
            get
            {
                return this._subQueries == null ? 0 : this._subQueries.Count;
            }
        }
        public int PositionedVarCount
        {
            get
            {
                return this._positionedVariables == null ? 0 : this._positionedVariables.Count;
            }
        }
        public bool IsTopStatement
        {
            get { return this._isTopStatement; }
        }
        public bool NeedParen
        {
            get { return this._needParen; }
            set { this._needParen = value; }
        }

        public QueryStatement this[int index]
        {
            get
            {
                if (!this._isCompiled || index < 0 || index >= this._subQueries.Count) return null;
                return this._subQueries[index];
            }
        }

        public virtual int ColumnCountInSelect
        {
            get
            {
                return -1;
            }
        }
        public virtual bool IsTableAlias(string table)
        {
            return this.IsTableAlias(table, false);
        }
        public virtual bool IsTableAlias(string table, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(table)) return false;

            if (this._tables != null && this._tables.Count > 0)
                foreach (Table t in this._tables)
                    if (t.HasAlias && t.AliasName == table) return true;

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    bool r = q.IsTableAlias(table, subQuery);
                    if (r) return r;
                }

            return false;
        }
        public virtual string TableAlias(string table)
        {
            return this.TableAlias(table, false);
        }
        public virtual string TableAlias(string table, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(table)) return string.Empty;

            if (this._tables != null && this._tables.Count > 0)
                foreach (Table t in this._tables)
                    if (t.HasAlias && t.TableName == table) return t.AliasName;

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    string s = q.TableAlias(table, subQuery);
                    if (!string.IsNullOrEmpty(s)) return s;
                }

            return string.Empty;
        }
        public virtual string TableNameByAlias(string alias)
        {
            return this.TableNameByAlias(alias, false);
        }
        public virtual string TableNameByAlias(string alias, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(alias)) return null;

            if (this._tables != null && this._tables.Count > 0)
                foreach (Table t in this._tables)
                    if (t.HasAlias && t.AliasName == alias) return t.TableName; //the table name may be null, in case of subqueries

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    //if sub queries find the table alias, and it is a subquery(returns null), how to terminate this search process?
                    string s = q.TableNameByAlias(alias, subQuery);
                    if (!string.IsNullOrEmpty(s)) return s;
                }

            return null;
        }
        public virtual string TableRefName(string table)
        {
            return this.TableRefName(table, false);
        }
        public virtual string TableRefName(string table, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(table)) return null;

            if (this._tables != null && this._tables.Count > 0)
                foreach (Table t in this._tables)
                    if (t.HasTableName && t.TableName == table)
                    {
                        if (t.HasAlias) return t.AliasName;
                        else return t.TableName; //the table name may be null, in case of subqueries
                    }

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    string s = q.TableRefName(table, subQuery);
                    if (!string.IsNullOrEmpty(s)) return s;
                }

            return null;
        }
        public virtual bool IsColumnAlias(string column)
        {
            return this.IsColumnAlias(column, false);
        }
        public virtual bool IsColumnAlias(string column, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(column)) return false;

            if (this._columns != null && this._columns.Count > 0)
                foreach (Column t in this._columns)
                    if (t.HasAlias && t.AliasName == column) return true;

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    bool r = q.IsColumnAlias(column, subQuery);
                    if (r) return r;
                }

            return false;
        }
        public virtual string ColumnNameByAlias(string alias)
        {
            return this.ColumnNameByAlias(alias, false);
        }
        public virtual string ColumnNameByAlias(string alias, bool subQuery)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(alias)) return null;

            if (this._columns != null && this._columns.Count > 0)
                foreach (Column t in this._columns)
                    if (t.HasAlias && t.AliasName == alias) return t.ColumnName; //the column name may be null, in case of functions, expressions

            if (subQuery && this._subQueries != null && this._subQueries.Count > 0)
                foreach (QueryStatement q in this._subQueries)
                {
                    string s = q.ColumnNameByAlias(alias, subQuery);
                    if (!string.IsNullOrEmpty(s)) return s;
                }

            return null;
        }
        private Table TryGetTable(string nameOrAlias)
        {
            if (!this._isCompiled || string.IsNullOrEmpty(nameOrAlias) || this._tables == null || this._tables.Count <= 0) return null;
            Table t1 = null;
            foreach (Table t in this._tables)
                if (t.HasAlias && t.AliasName == nameOrAlias) { t1 = t; break; }
                else if (!t.HasAlias && t.HasTableName && t.TableName == nameOrAlias) { t1 = t; break; }
            return t1;
        }
        /// <summary>
        /// Try to find out which table the column comes from.
        /// </summary>
        /// <param name="column">The column name that will be seached for.</param>
        /// <param name="result">If exactly found, it stores the table name if exists(in the case of an alias for a constant, the result may be null).</param>
        /// <param name="guess">Is this method call comming in a "just have a try" round?</param>
        /// <param name="guessingList">Although it may not exactly find the target, some unidentified objects probably exist there.</param>
        /// <returns>Whether exactly find out the target.</returns>
        public NameResolverResult IsThisColumnMine(string column, out string tableName, ref IList<string> maybeHere)
        {
            tableName = null;
            if (!this._isCompiled || this._columns == null || this._columns.Count <= 0) return NameResolverResult.No;
            if (this._tables == null || this._tables.Count <= 0) return NameResolverResult.No; //mostly it's an error SQL in this case

            IList<Column> guessingColumns = new List<Column>();
            Column col = null;
            //although most database support return multiple column with the same name, I ignore this case currently.
            bool lookupAllTables = false;
            foreach (Column c in this._columns)
                if (c.HasAlias && c.AliasName == column) col = c;
                else if (!c.HasAlias && c.ColumnName == column) col = c;
                else if (c.IsAllColumn && c.HasTableName)
                {
                    guessingColumns.Add(c);
                    lookupAllTables = true;
                }
                else if (c.IsAllColumn) lookupAllTables = true;

            //found!!! the column is an alias from a function, constant, user variable, or subquery, no need for mapping handling
            if (col != null && !col.IsIdentifier) { guessingColumns.Clear(); return NameResolverResult.Yes; }

            //column found in select list, and it is identified by a table or alias prefix.
            if (col != null && col.HasTableName)
            {
                guessingColumns.Clear();
                guessingColumns = null;
                maybeHere.Clear();

                Table table = this.TryGetTable(col.TableName);
                if (table == null) return NameResolverResult.MustBe; //mostly it's an error SQL in this case
                if (!table.IsSubQuery) { tableName = table.TableName; return NameResolverResult.Yes; }
                //it's another sub query, just try to find in this query.
                NameResolverResult result = (table._children[0] as QueryStatement).IsThisColumnMine(col.ColumnName, out tableName, ref maybeHere);
                if (result == NameResolverResult.Maybe && maybeHere.Count == 1)
                {
                    tableName = maybeHere[0];
                    maybeHere.Clear();
                    result = NameResolverResult.Yes;
                }
                else if (result == NameResolverResult.No)
                {
                    maybeHere.Clear();
                    result = NameResolverResult.MustBe;
                }
                else if (result == NameResolverResult.Maybe)
                    result = NameResolverResult.MustBe;
                return result;
            }

            //column not occurs in select cluase, and no guessing columns, where does the column come from?
            if (col == null && guessingColumns.Count <= 0 && !lookupAllTables) { guessingColumns.Clear(); return NameResolverResult.No; }

            //let's get the guessing list first
            IList<Table> guessingTables = new List<Table>();
            if (guessingColumns != null && guessingColumns.Count > 0)
            {
                //guessingColumns.Count is great than 0, which means such as a "select a.* " occurs in select clause.
                //in this case those tables associated with the guessing columns will be considered first
                foreach (Column c in guessingColumns)
                {
                    Table temp_t = this.TryGetTable(c.HasAlias ? c.AliasName : c.TableName);
                    if (temp_t != null)
                        guessingTables.Add(temp_t);
                }
                guessingColumns.Clear();
            }
            guessingColumns = null;
            if (col != null || lookupAllTables)
                //(col != null && col.HasTableName) has been handled on above.
                //so here must be on the situation: 
                //1. the column exists in select clause, but without a table or an alias identified, 
                //2. or a "select * " occurs in select clause.
                foreach (Table t in this._tables)
                    if (!guessingTables.Contains(t))
                        guessingTables.Add(t);

            if (col != null) maybeHere.Clear(); //col!=null means the column must come from this query
            //we lookup in each guessing table, try to find something out.
            for (int i = 0; i < guessingTables.Count; i++)
            {
                if (!guessingTables[i].IsSubQuery) maybeHere.Add(guessingTables[i].TableName);
                else
                {
                    QueryStatement sub = guessingTables[i]._children[0] as QueryStatement;
                    NameResolverResult result = sub.IsThisColumnMine(col == null ? column : col.ColumnName, out tableName, ref maybeHere);
                    if (result == NameResolverResult.Yes || result == NameResolverResult.MustBe)
                    {
                        guessingTables.Clear();
                        return result;
                    }
                }
            }

            guessingTables.Clear();
            guessingTables = null;
            if (col != null && maybeHere.Count == 1)
            {
                tableName = maybeHere[0];
                maybeHere.Clear();
                return NameResolverResult.Yes;
            }
            else if (col != null)
                return NameResolverResult.MustBe;

            return NameResolverResult.Maybe;
        }
        public void AddOrderBy(string columnName, string method)
        {
            if (this._children == null || this._children.Count <= 0) return;
            OrderByClause clause = null;
            if (OrderByClause.IsOrderByClause(this._children[this._children.Count - 1]))
                clause = this._children[this._children.Count - 1] as OrderByClause;
            else
            {
                clause = OqlUtil.CreateOrderBy();
                this._children.Add(clause);
            }
            clause.AddOrderBy(columnName, method);
        }
        public void AddOrderBy(string columnName)
        {
            this.AddOrderBy(columnName, null);
        }
        public bool HasOrderByClause
        {
            get
            {
                foreach (Expression exp in this)
                    if (OrderByClause.IsOrderByClause(exp)) return true;
                return false;
            }
        }

        #region SqlVisitorInterceptor
        /// <summary>
        /// Ask the expression owner(a SelectStatement or UnionStatement, it is the scope in which the table or column name plays)
        /// whether the table or column name is an alias, if it is then announce the original visitor with Sql() method call, else just forwards
        /// the Table() or Column() method call.
        /// </summary>
        protected class SqlVisitorInterceptor : ISqlVisitor
        {
            //the RequiredClause for SqlVisitorInterceptor is always RequiredClause.All
            //this means all the subquery will out put all clauses.
            //this flag is exposed to the client and only the top query will consider it.
            private RequiredClause _clauseFlag = RequiredClause.All;
            private ISqlVisitor _visitor;
            private QueryStatement _parent;
            public SqlVisitorInterceptor(ISqlVisitor visitor, QueryStatement parent)
            {
                this._visitor = visitor;
                this._parent = parent;
            }

            public void Sql(string sql)
            {
                this._visitor.Sql(sql);
            }
            public void NamedVariable(string varName)
            {
                this._visitor.NamedVariable(varName);
            }
            public void PositionedVariable(int index)
            {
                this._visitor.PositionedVariable(index);
            }
            public void Space()
            {
                this._visitor.Space();
            }

            public void Table(string name)
            {
                if (string.IsNullOrEmpty(name)) return;
                if (this._parent.IsTableAlias(name)) this._visitor.Sql(name);
                else
                {
                    //break the interceptor chain.
                    this.TableOriginal(name);
                }
            }
            public void TableOriginal(string name)
            {
                SqlVisitorInterceptor interceptor = this._visitor as SqlVisitorInterceptor;
                if (interceptor == null) this._visitor.Table(name);
                else
                    interceptor.TableOriginal(name);
            }
            //TODO
            //如果select list里面指定了列的别名，这个列就只能用别名访问，但是使用table.column而不是别名同样可以访问。
            //这里需要考虑的情况比较复杂:
            //1. SELECT a1.col1 AS C1, a2.col2 AS col1
            //2. select Org_ID as a1,Org_Code as org_id from dbo.SYS_Org as a ORDER BY Org_ID
            private void ColumnDecision(QueryStatement q, string table, string column, bool iAmSure)
            {
                string logDesc = string.IsNullOrEmpty(table) ? column : (table + "." + column);
                //Log.Debug(this.GetType(), "Property Name Resolver: {0}", logDesc);

                if (string.IsNullOrEmpty(column)) return; //error
                if (q._tables == null || q._tables.Count <= 0) //error
                {
                    //Log.Error(this.GetType(), "No table exists in query, Mapping Resolver is disabled.");
                    this._visitor.Sql(column);
                    return;
                }
                if (q._tables.Count == 1 && !q._tables[0].IsSubQuery) //simple case
                {
                    //Log.Debug(this.GetType(), "Property {0} may come from object {1}", column, q._tables[0].TableName);
                    this.ColumnOriginal(q._tables[0].TableName, column, iAmSure);
                    return;
                }

                //to solve a problem:
                //SELECT a.user_code AS User_Name ... FROM Tbl_User AS a WHERE a.user_name=@username
                //in where clause, the a.user_name would trigger a Column() call instead of Sql(). 
                //because the prefix a. has identified that the user_name here is a real column, not referring the alias in select clause.
                if (!iAmSure && q.IsColumnAlias(column) && string.IsNullOrEmpty(table))
                // !guess && q.IsColumnAlias(column), make no sense in this case, just as the example sql 2. above.
                {
                    //Log.Debug(this.GetType(), "Property {0} is an alias, it won't be translated", logDesc);
                    //if this call is not triggered in a select list, and the column name is an alias, just emit a Sql() call.
                    this._visitor.Sql(column);
                    return;
                }

                Table t = null;
                if (!string.IsNullOrEmpty(table))
                {
                    t = q.TryGetTable(table);
                    if (t == null)
                    {
                        //Log.Error(this.GetType(), "Object \"{0}\" in \"{1}\" cannot be found in this query", table, logDesc);
                        this.ColumnOriginal(table, column, iAmSure);
                        return;
                    } //if t is null, we have nothing to do.
                }

                //if the table is not a subquery, I think we just get the real table and column name.
                if (t != null && !t.IsSubQuery)
                {
                    //Log.Debug(this.GetType(), "Object found, object name: {0}, property name: {1}", t.TableName, column);
                    this.ColumnOriginal(t.TableName, column, iAmSure);
                    return;
                }

                //Log.Debug(this.GetType(), "Try advanced Property Name Resolving strategies to identify which object that the property {0} comes from.", logDesc);
                QueryStatement sub = null;
                if (string.IsNullOrEmpty(table)) sub = q;
                else sub = t._children[0] as QueryStatement;
                if (!iAmSure && sub.IsColumnAlias(column))
                {
                    //Log.Debug(this.GetType(), "Property {0} is an alias in an sub query, it won't be translated", logDesc);
                    this._visitor.Sql(column);
                    return;
                } //alias in sub query

                string s = null;
                IList<string> maybe = new List<string>();
                NameResolverResult seachResult;

                if (iAmSure && t == null)
                {
                    //Log.Debug(this.GetType(), "Try to lookup objects or sub queries in from clause to identify where property {0} comes from.", logDesc);
                    foreach (Table tbl in this._parent._tables)
                        if (tbl.IsSubQuery)
                        {
                            seachResult = (tbl._children[0] as QueryStatement).IsThisColumnMine(column, out s, ref maybe);
                            if (seachResult == NameResolverResult.Yes)
                            {
                                if (string.IsNullOrEmpty(s))
                                {
                                    //Log.Debug(this.GetType(), "Property {0} found in sub query {1}, it's an alias in the sub query.", logDesc, tbl.AliasName);
                                    this._visitor.Sql(column);
                                }
                                else
                                {
                                    //Log.Debug(this.GetType(), "Property {0} found in sub query {1}, object name: {2}.", logDesc, tbl.AliasName, s);
                                    this.ColumnOriginal(s, column, iAmSure);
                                }
                                return;
                            }
                            else if (seachResult == NameResolverResult.MustBe)
                            {
                                if (maybe.Count <= 0)
                                {
                                    //Log.Debug(this.GetType(), "Property {0} found in sub query {1}, but it cannot be identified which object it comes from.", logDesc, tbl.AliasName);
                                    this.Sql(column);
                                }
                                else if (maybe.Count == 1)
                                {
                                   // Log.Debug(this.GetType(), "Property {0} found in sub query {1}, object name: {2}.", logDesc, tbl.AliasName, maybe[0]);
                                    this.ColumnOriginal(maybe[0], column, iAmSure);
                                }
                                else if (maybe.Count > 1)
                                {
                                    StringBuilder error = new StringBuilder();
                                    error.Append("Property ").Append(logDesc).Append(" is ambiguous, it may comes from {{");
                                    foreach (string sTable in maybe)
                                        error.Append(sTable).Append(",");
                                    error.Remove(error.Length - 1, 1);
                                    error.Append("}}, please make the reference clearly.");
                                    //Log.Warn(this.GetType(), error.ToString());
                                    this.ColumnOriginal(null, column, iAmSure);
                                }
                                return;
                            }
                        }
                   // Log.Info(this.GetType(), "ObjectQuery cannot find which object that property {0} belongs to, this can be resolved by using an alias such as \"a.{1}\"", logDesc, logDesc);
                    this.ColumnOriginal(null, column, iAmSure);
                    return;
                }

                seachResult = sub.IsThisColumnMine(column, out s, ref maybe);
                #region handle result from IsThisColumnMine
                if (seachResult == NameResolverResult.Yes)
                {
                    //find out the real table if s is not null, else the column is not a real column name, it's just an alias
                    if (string.IsNullOrEmpty(s))
                    {
                        //Log.Debug(this.GetType(), "Property {0} is an alias in an sub query, it won't be translated", logDesc);
                        this._visitor.Sql(column);
                    }
                    else
                    {
                        //Log.Debug(this.GetType(), "Object found, object name: {0}, property name: {1}", s, column);
                        this.ColumnOriginal(s, column, iAmSure);
                    }
                }
                else if (seachResult == NameResolverResult.No)
                {
                    //Log.Error(this.GetType(), "Property {0} cannot be found in this query, please correct your query.", logDesc);
                    //this is an error OQL
                    this._visitor.Sql(column);
                }
                else
                {
                    if (maybe.Count == 1)
                    {
                        //Log.Debug(this.GetType(), "Object found, object name: {0}, property name: {1}", maybe[0], column);
                        this.ColumnOriginal(maybe[0], column, iAmSure);
                    }
                    else if (maybe.Count <= 0) //this is an error OQL
                    {
                        //Log.Error(this.GetType(), "Property {0} cannot be found in this query, please correct your query.", logDesc);
                        this._visitor.Sql(column);
                    }
                    else
                    {
                        //StringBuilder error = new StringBuilder();
                        //error.Append("Property ").Append(logDesc).Append(" is ambiguous, it may comes from {{");
                        //foreach (string sTable in maybe)
                        //    error.Append(sTable).Append(",");
                        //error.Remove(error.Length - 1, 1);
                        //error.Append("}}, please make the reference clearly.");
                        //Log.Warn(this.GetType(), error.ToString());
                        this.Column(maybe, column);
                    }
                }
                #endregion
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="table"></param>
            /// <param name="name"></param>
            /// <param name="guess">
            /// <para>false: The column name is from a select, insert, update list, it's mostly a true column name</para>
            /// <para>true: The column name is from other clauses, it maybe a true column name, or an alias.</para>
            /// </param>
            public void Column(string table, string name, bool iAmSure)
            {
                this.ColumnDecision(this._parent, table, name, iAmSure);
            }
            /// <summary>
            /// The column maybe comes from the <paramref name="tables"/>
            /// </summary>
            /// <param name="tables"></param>
            /// <param name="name"></param>
            public void Column(IList<string> tables, string name)
            {
                this._visitor.Column(tables, name);
            }
            public void ColumnOriginal(string table, string name, bool iAmSure)
            {
                SqlVisitorInterceptor interceptor = this._visitor as SqlVisitorInterceptor;
                if (interceptor == null) this._visitor.Column(table, name, iAmSure);
                else interceptor.ColumnOriginal(table, name, iAmSure);
            }

            /// <summary>
            /// Which clauses the ISqlVisitor is requesting for.
            /// </summary>
            public RequiredClause ClauseFlag
            {
                get
                {
                    return this._clauseFlag;
                }
                set
                {
                    this._clauseFlag = value;
                }
            }

            public void Close()
            {
                this._visitor = null;
                this._parent = null;
            }
        }
        #endregion
    }
}